package com.lyy.lottery.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.lyy.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.lyy.lottery.common.exception.ServiceException;
import com.lyy.lottery.common.util.JWTUtil;
import com.lyy.lottery.common.util.RegexUtil;
import com.lyy.lottery.controller.param.ShortMessageLoginParam;
import com.lyy.lottery.controller.param.UserLoginParam;
import com.lyy.lottery.controller.param.UserPasswordLoginParam;
import com.lyy.lottery.controller.param.UserRegisterParam;
import com.lyy.lottery.dao.dataobject.Encrypt;
import com.lyy.lottery.dao.dataobject.UserDo;
import com.lyy.lottery.dao.mapper.UserMapper;
import com.lyy.lottery.service.UserService;
import com.lyy.lottery.service.VerificationCodeService;
import com.lyy.lottery.service.dto.UserDTO;
import com.lyy.lottery.service.dto.UserLoginDTO;
import com.lyy.lottery.service.dto.UserRegisterDTO;
import com.lyy.lottery.service.enums.UserIdentityEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private VerificationCodeService verificationCodeService;

    //用户注册
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {

        //校验注册信息
        checkRegisterInfo(param);
        //加密私密数据（构造dao层请求）
        UserDo userDo=new UserDo();
        userDo.setUserName(param.getName());
        userDo.setIdentity(param.getIdentity());
        userDo.setEmail(param.getMail());
        userDo.setPhoneNumber(new Encrypt(param.getPhoneNumber()));
        if (StringUtils.hasText(param.getPassword())){
            userDo.setPassword(DigestUtil.sha256Hex(param.getPassword()));
        }
        //保存数据
        userMapper.insert(userDo);//插入后将主键放到userDo
        //构造返回
        UserRegisterDTO userRegisterDTO=new UserRegisterDTO();
        userRegisterDTO.setUserId(userDo.getId());
        return userRegisterDTO;
    }

    //用户登录
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO userLoginDTO;
        //instanceof通过对象的运行时类型来判断一个对象是否是指定类或其子类的实例，以避免在类型转换时发生ClassCastException异常
        //1、判断登录方式（密码/验证码）
        if (param instanceof UserPasswordLoginParam){
            UserPasswordLoginParam loginParam=(UserPasswordLoginParam) param;
            userLoginDTO=loginByPassword(loginParam);
        }else if (param instanceof ShortMessageLoginParam){
            ShortMessageLoginParam loginParam=(ShortMessageLoginParam) param;
            userLoginDTO=loginByShortMessage(loginParam);
        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXITS);
        }
        return userLoginDTO;
    }

    private void checkRegisterInfo(UserRegisterParam param) {
        if (null == param){
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }
        //校验邮箱格式
        if (!RegexUtil.checkMail(param.getMail())){
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }
        //校验手机号格式
        if (!RegexUtil.checkMobile(param.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }
        //校验身份信息
        if (null== UserIdentityEnum.forName(param.getIdentity())){
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        //校验管理员密码必填
        if (param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
                && !StringUtils.hasText(param.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
        }
        //校验密码格式
        if (StringUtils.hasText(param.getPassword())
                && !RegexUtil.checkPassword(param.getPassword())){
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }
        //校验邮箱是否被使用
        if (checkMailUsed(param.getMail())){
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }
        //校验手机号是否被使用
        if (checkPhoneNumber(param.getPhoneNumber())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }

    private boolean checkMailUsed(String email) {
        int count=userMapper.countByMail(email);
        return count > 0;
    }

    private boolean checkPhoneNumber(String phoneNumber) {
        int count =userMapper.countByPhone(new Encrypt(phoneNumber));
        return count>0;
    }

    private UserLoginDTO loginByPassword(UserPasswordLoginParam loginParam) {
        UserDo userDo;
        //1、判断手机号登录还是邮箱登录
        if (RegexUtil.checkMail(loginParam.getLoginName())){
            //根据邮箱查询用户表
            userDo=userMapper.selectByMail(loginParam.getLoginName());
        }else if (RegexUtil.checkMobile(loginParam.getLoginName())){
            //根据手机号查询用户表
            userDo=userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginName()));
        }else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXITS);
        }

        //2、校验用户信息
        if (null==userDo){
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        }else if (StringUtils.hasText(loginParam.getMandatoryIdentity()) && !loginParam.getMandatoryIdentity().equals(userDo.getIdentity())){
            //身份未填写 或 身份与数据库用户身份不一致
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        } else if (!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDo.getPassword())) {
            //密码错误
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        //3、塞入返回值
        Map<String,Object> claim=new HashMap<>();
        //令牌可加密也可解密，不要放隐私数据
        claim.put("id",userDo.getId());
        String token=JWTUtil.genJwt(claim);

        UserLoginDTO userLoginDTO=new UserLoginDTO();
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDo.getIdentity()));
        userLoginDTO.setToken(token);
        return userLoginDTO;
    }

    private UserLoginDTO loginByShortMessage(ShortMessageLoginParam loginParam) {
        //1、校验手机号
        if (!RegexUtil.checkMobile(loginParam.getLoginMobile())){
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        //2、校验登录信息
        UserDo userDo=userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginMobile()));
        if (null==userDo){
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        } else if (StringUtils.hasText(loginParam.getMandatoryIdentity())
                    && !loginParam.getMandatoryIdentity()
                       .equals(userDo.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        //3、校验验证码
        String code=verificationCodeService.getVerificationCode(loginParam.getLoginMobile());
        //从缓存中获取的验证码可能为空，而用户传入的参数是规定了不能为空的，为防止空指针异常，放入括号中
        if (!loginParam.getVerificationCode().equals(code)){
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);
        }

        //4、塞入返回值
        Map<String,Object> claim=new HashMap<>();
        claim.put("id",userDo.getId());
        String token=JWTUtil.genJwt(claim);

        UserLoginDTO userLoginDTO=new UserLoginDTO();
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDo.getIdentity()));
        userLoginDTO.setToken(token);
        return userLoginDTO;
    }

    @Override
    public List<UserDTO> findUserInfo(UserIdentityEnum userIdentityEnum) {
        String identity= null==userIdentityEnum ? null :userIdentityEnum.name();
        //查表
        List<UserDo> userDoList=userMapper.selectUserListByIdentity(identity);
        //类型转换
        List<UserDTO> userDTOList=userDoList.stream()
                .map(userDo -> {
                    UserDTO userDTO=new UserDTO();
                    userDTO.setUserName(userDo.getUserName());
                    userDTO.setUserId(userDo.getId());
                    userDTO.setEmail(userDo.getEmail());
                    userDTO.setPhoneNumber(userDo.getPhoneNumber().getValue());
                    userDTO.setIdentity(UserIdentityEnum.forName(userDo.getIdentity()));
                    return userDTO;
                }).collect(Collectors.toList());
        return userDTOList;
    }
}




















